﻿using System;
using System.Configuration;
using System.Diagnostics;
using SimpleServiceBus.Bus;
using SimpleServiceBus.Configuration;
using SimpleServiceBus.Endpoint;
using SimpleServiceBus.EndpointManagement.Server;
using SimpleServiceBus.EndpointManagement.Server.Configuration;
using SimpleServiceBus.EndpointManagement.Server.Wcf;
using SimpleServiceBus.Endpoints.Msmq;
using SimpleServiceBus.Endpoints.Msmq.Configuration;
using SimpleServiceBus.Persistence.Domain.Management.Repositories;
using SimpleServiceBus.Persistence.NHibernate;
using SimpleServiceBus.Persistence.NHibernate.Repositories;
using SimpleServiceBus.StructureMap;
using SimpleServiceBus.Utilities;
using SimpleServiceBus.Utilities.Email;
using SimpleServiceBus.Utilities.ObjectBuilder;
using StructureMap;

namespace SimpleServiceBus.EndpointMonitorService
{
    public class EndpointMonitorServerHost : IBackgroundService
    {
        public event EventHandler<EndpointStatusEventArgs> EndpointStatusExpired;
        public event EventHandler<EndpointStatusEventArgs> EndpointStatusUpdated;

        public event EventHandler<MessageProcessingErrorEventArgs> MessageProcessingError;

        public event EventHandler<EndpointStatusSummaryEventArgs> EndpointStatusSummaryUpdated;

        private IMessagingEndpoint _endpoint;
        private EndpointManagementWebServiceHost _webService;

        public void Dispose()
        {
            if (IsRunning)
                Stop();
        }

        public bool IsRunning
        {
            get { return _endpoint != null && _endpoint.IsRunning; }
        }

        public virtual bool EnablePersistence
        {
            get
            {
                string enabled = ConfigurationManager.AppSettings["enablePersistence"];
                if (!String.IsNullOrEmpty(enabled))
                    return Convert.ToBoolean(enabled);
                return false;
            }
        }

        public virtual bool PersistIncrementalPerformanceMeasurements
        {
            get
            {
                string enabled = ConfigurationManager.AppSettings["persistIncrementalPerformanceMeasurements"];
                if (!String.IsNullOrEmpty(enabled))
                    return Convert.ToBoolean(enabled);
                return false;
            }
        }

        public virtual bool UpdateSchemaOnStart
        {
            get
            {
                string enabled = ConfigurationManager.AppSettings["updateSchemaOnStart"];
                if (!String.IsNullOrEmpty(enabled))
                    return Convert.ToBoolean(enabled);
                return false;
            }
        }

        public void Start()
        {
            IObjectBuilder objectBuilder = CreateObjectBuilder();
            
            //Create the endpoint
            if (_endpoint == null)
            {

                IEndpointBuilder builder = MsmqEndpointConfiguration.ConfigureEndpoint(objectBuilder);
                EndpointMonitorConfiguration.ConfigureEndpointAsEndpointMonitor(builder);
                builder.MessageHandlers.RegisterMessageHandlerAssembly(GetType().Assembly);
                _endpoint = builder.Endpoint;

                //Ensure requisite queues are created
                MsmqHelper.EnsureQueue(_endpoint.Metadata.ReturnAddress);
                if (!String.IsNullOrEmpty(_endpoint.Metadata.PoisonMessageAddress))
                    MsmqHelper.EnsureQueue(_endpoint.Metadata.PoisonMessageAddress);

                //Attach to bus events
                _endpoint.MessageBus.MessageProcessingException += OnMessageProcessingError;
                
                //Attach to monitor events
                EndpointMonitor.EndpointStatusUpdated += OnEndpointStatusUpdated;
                EndpointMonitor.EndpointStatusExpired += OnEndpointStatusExpired;

                //Attach to aggregator events
                if (StatusAggregator != null)
                {
                    StatusAggregator.EndpointStatusSummaryUpdated+=OnEndpointStatusSummaryUpdated;
                }
                
            }

            if (EnablePersistence)
            {
                ConfigurePersistence();
                ConfigureWebService(objectBuilder);
            }


            if (!_endpoint.IsRunning)
                _endpoint.Start();
        }
       

        public void Stop()
        {
            if (_webService != null)
                _webService.Close();
            
            if (_endpoint != null && _endpoint.IsRunning)
                _endpoint.Stop();
        }

        protected virtual void ConfigurePersistence()
        {
            SchemaGenerationOptions schemaOptions = UpdateSchemaOnStart
                                                           ? SchemaGenerationOptions.UpdateSchema
                                                           : SchemaGenerationOptions.None;

            DataSessionManager.Create<ThreadStaticSession>(schemaOptions);

            //Set up a data session Unit Of Work factory so that messages being processed
            //by the bus will have a current data session to work with
            _endpoint.LocalServices.RegisterService<IUnitOfWorkFactoryService>(new DataSessionUnitOfWorkFactoryService());

            //So we can store the status reports in the database
            _endpoint.LocalServices.RegisterService<IEndpointStatusRepository>(
                new EndpointStatusRepository
                    {
                        PersistIncrementalStatusUpdates = PersistIncrementalPerformanceMeasurements
                    });

            _endpoint.LocalServices.RegisterService<IManagedEndpointRepository>(new ManagedEndpointRepository());

        }

        protected virtual void ConfigureWebService(IObjectBuilder objectBuilder)
        {
            string uri = "http://localhost:8888";
            EndpointMonitorConfiguration config = EndpointMonitorConfiguration.GetConfiguration();
            if (config != null && !String.IsNullOrEmpty(config.WebServiceUri))
                uri = config.WebServiceUri;

            _webService = new EndpointManagementWebServiceHost(objectBuilder,()=>new DataSessionUnitOfWork(),new Uri(uri));
            _webService.Open();
        }

        //Override this method to supply a different object builder
        protected virtual IObjectBuilder CreateObjectBuilder()
        {
            ObjectFactory.Initialize(init =>
                                         {
                                             init.ForRequestedType<IManagedEndpointRepository>()
                                                 .TheDefaultIsConcreteType<ManagedEndpointRepository>();
                                             init.ForRequestedType<IEndpointErrorRepository>()
                                                 .TheDefaultIsConcreteType<EndpointErrorRepository>();
                                             init.ForRequestedType<IEndpointPerformanceSummaryRepository>()
                                                 .TheDefaultIsConcreteType<EndpointPerformanceSummaryRepository>();
                                             init.ForRequestedType<IContractMapper>()
                                                 .TheDefaultIsConcreteType<AutoMapperContractMapper>();
                                         });
            
            return new StructureMapObjectBuilder();
        }
        

        protected IEndpointMonitorServerService EndpointMonitor
        {
            get
            {
                if (_endpoint == null)
                    return null;
                return _endpoint.LocalServices.GetService<IEndpointMonitorServerService>();
            }
        }

        protected IEndpointStatusAggregationService StatusAggregator
        {
            get
            {
                if (_endpoint == null ||
                    !_endpoint.LocalServices.HasService<IEndpointStatusAggregationService>())
                    return null;
                return _endpoint.LocalServices.GetService<IEndpointStatusAggregationService>();
            }
        }
        
        protected virtual void OnEndpointStatusExpired(object sender, EndpointStatusEventArgs args)
        {
            EventHandler<EndpointStatusEventArgs> evt = EndpointStatusExpired;
            if (evt != null)
                evt(sender, args);
        }

        protected virtual void OnEndpointStatusUpdated(object sender, EndpointStatusEventArgs args)
        {
            EventHandler<EndpointStatusEventArgs> evt = EndpointStatusUpdated;
            if (evt != null)
                evt(sender, args);
        }

        protected virtual void OnMessageProcessingError(object sender, MessageProcessingErrorEventArgs args)
        {
            EventLog.WriteEntry("SimpleServiceBus Endpoint Monitor","An error occurred processing a message:" + args.Exception);
            EventHandler<MessageProcessingErrorEventArgs> evt = MessageProcessingError;
            if (evt != null)
                evt(sender, args);
        }

        protected virtual void OnEndpointStatusSummaryUpdated(object sender,  EndpointStatusSummaryEventArgs args)
        {
            EventHandler<EndpointStatusSummaryEventArgs> evt = EndpointStatusSummaryUpdated;
            if (evt != null)
                evt(sender, args);
        }

    }
}
